﻿namespace EyeOpen.UI.Silverlight
{
    using System;
    using System.Globalization;
    using System.Windows.Data;

    /// <summary>
    /// Represents a generic ValueConverter.
    /// </summary>
    /// <typeparam name="T">The type of the value to convert.</typeparam>
    /// <typeparam name="TResult">The type of the result.</typeparam>
    public abstract class ValueConverter<T, TResult> : IValueConverter
    {
        /// <summary>
        /// Converts a value.
        /// </summary>
        /// <param name="value">The value produced by the binding source.</param>
        /// <param name="targetType">The type of the binding target property.</param>
        /// <param name="parameter">The converter parameter to use.</param>
        /// <param name="culture">The culture to use in the converter.</param>
        /// <returns>
        /// A converted value. If the method returns null, the valid null value is used.
        /// </returns>
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value != null)
            {
                CheckTypeMatch<T>(value.GetType(), "value");
            }

            CheckTypeMatch<TResult>(targetType, "targetType");

            return Convert(CastValueToGeneric<T>(value), CastValueToGeneric<T>(parameter), culture);
        }

        /// <summary>
        /// Converts a value to the retult type.
        /// </summary>
        /// <param name="value">The value that is produced by the binding target.</param>
        /// <param name="targetType">The type to convert to.</param>
        /// <param name="parameter">The converter parameter to use.</param>
        /// <param name="culture">The culture to use in the converter.</param>
        /// <returns>
        /// A converted value. If the method returns null, the valid null value is used.
        /// </returns>
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            CheckTypeMatch<T>(value.GetType(), "value");

            CheckTypeMatch<TResult>(targetType, "targetType");

            return ConvertBack(CastValueToGeneric<TResult>(value), CastValueToGeneric<TResult>(parameter), culture);
        }

        /// <summary>
        /// Converts the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="parameter">The parameter.</param>
        /// <param name="culture">The culture.</param>
        /// <returns></returns>
        protected abstract TResult Convert(T value, T parameter, CultureInfo culture);

        /// <summary>
        /// Converts the result type back to the source value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="parameter">The parameter.</param>
        /// <param name="culture">The culture.</param>
        /// <returns></returns>
        protected virtual T ConvertBack(TResult value, TResult parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        private static void CheckTypeMatch<TType>(Type type, string paramName)
        {
            if (type != typeof(object))
            {
                if (typeof(TType) != typeof(object))
                {
                    if (type != typeof(TType))
                    {
                        throw new ArgumentException("Parameter type is not valid.", paramName);
                    }
                }
            }
        }

        private static TRet CastValueToGeneric<TRet>(object value)
        {
            if (value == null)
            {
                return default(TRet);
            }

            return (TRet)value;
        }
    }
}
